В рамках данного проекта проводится разведочный анализ данных туристического сектора Таиланда за 2011-2020 годы (источник - Министрество Туризма и Спорта Королевства Таиланд).
По каждой стране прибытия известна информация о:
1. Среднем времени пребывания (в днях)
2. Количестве приезжающих первично/повторно (кол-во прибытий (пересечений границы))
3. Количестве приезжающих по суше/воде/воздуху (кол-во прибытий (пересечений границы))
4. Средней сумме ежедневных расходов по категориям (в тайских батах, THB)
5.* Для лучшего представления о странах прибытия также было решено добавить данные о ВВП (в долларах США, USD) этих стран на заданном временном промежутке (источник - Всемирный Банк).
Изначально данные предоставляются в отдельных таблицах Excel(.xlsx). Для каждой таблицы необходимо провести предобработку данных и для удобства последующего анализа объединить все данные в один датасет.
Дополнением к разведочному анализу в данном блокноте является интерактивный дашборд, созданный в Tableau.
Загрузка необходимых библиотек
import warnings
warnings.filterwarnings('ignore')
import pandas as pd
import numpy as np
import plotly.express as px
import scipy.stats
df = pd.read_excel(io=r"country_avg_length_stay.xlsx", engine='openpyxl')
df.Country = df.Country.apply(lambda x: x.replace("\xa0", ' ')) # Сразу решим проблему кодировки
df.sample(10)
| Continent | Country | 2011 | 2012 | 2013 | 2014 | 2015 | 2016 | 2017 | 2018 | 2019 | 2020 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 37 | South Asia | India | 6.90 | 7.15 | 7.14 | 7.22 | 7.64 | 7.56 | 7.45 | 7.39 | 7.43 | 9.32 |
| 15 | Europe | Austria | 14.75 | 16.06 | 16.16 | 15.99 | 16.19 | 16.04 | 16.51 | 16.59 | 16.55 | 18.29 |
| 26 | Europe | Sweden | 18.55 | 19.75 | 19.51 | 19.48 | 19.38 | 18.89 | 19.14 | 19.13 | 19.15 | 20.06 |
| 32 | The Americas | Brazil | 11.51 | 12.46 | 13.18 | 13.03 | 13.96 | 14.64 | 14.82 | 13.81 | 12.49 | 12.98 |
| 20 | Europe | Germany | 17.09 | 18.03 | 17.83 | 17.53 | 17.00 | 17.36 | 17.47 | 17.29 | 17.37 | 18.43 |
| 23 | Europe | Norway | 14.92 | 16.05 | 16.46 | 16.01 | 15.63 | 15.65 | 16.11 | 16.38 | 17.40 | 20.28 |
| 34 | The Americas | U.S.A. | 14.01 | 14.87 | 14.56 | 14.33 | 13.38 | 13.98 | 14.18 | 14.33 | 14.19 | 14.71 |
| 28 | Europe | United Kingdom | 17.35 | 18.13 | 17.80 | 17.13 | 17.29 | 17.90 | 18.25 | 17.71 | 17.83 | 18.79 |
| 45 | Middle East | Egypt | 8.40 | 9.15 | 9.78 | 9.83 | 10.63 | 10.75 | 11.14 | 11.28 | 11.31 | 20.23 |
| 12 | East Asia | South Korea | 7.30 | 7.64 | 7.66 | 7.44 | 7.72 | 7.72 | 7.60 | 7.22 | 7.20 | 10.12 |
Пропусков в данных нет
df.isna().sum()
Continent 0 Country 0 2011 0 2012 0 2013 0 2014 0 2015 0 2016 0 2017 0 2018 0 2019 0 2020 0 dtype: int64
В таблице содержится информация по следующим странам
df.Country.sort_values().unique()
array(['Argentina', 'Australia', 'Austria', 'Bangladesh', 'Belgium',
'Brazil', 'Brunai', 'Cambodia', 'Canada', 'China', 'Denmark',
'East Europe', 'Egypt', 'Finland', 'France', 'Germany',
'Hong Kong', 'India', 'Indonesia', 'Israel', 'Italy', 'Japan',
'Kuwait', 'Laos', 'Malaysia', 'Myanmar', 'Nepal', 'Netherlands',
'New Zealand', 'Norway', 'Others', 'Pakistan', 'Philippines',
'Republic of South Africa', 'Russia', 'Saudi Arabia', 'Singapore',
'South Korea', 'Spain', 'Sri Lanka', 'Sweden', 'Switzerland',
'Taiwan', 'U.S.A.', 'United Arab Emirates', 'United Kingdom',
'Vietnam'], dtype=object)
Следует исключить из столбца "Country" Восточную Европу и категорию "Другие", т.к. нельзя сравнивать их с отдельными странами. Также переведем таблицу из широкого формата в длинный (для последующего соединения таблиц).
df = df.query("Country not in ('East Europe','Others')")
stay_len = pd.melt(df, id_vars='Country', value_vars=range(2011, 2021), var_name='Year', value_name='Average length of stay (days)').sort_values(['Country', 'Year'])
stay_len
| Country | Year | Average length of stay (days) | |
|---|---|---|---|
| 28 | Argentina | 2011 | 11.91 |
| 73 | Argentina | 2012 | 12.51 |
| 118 | Argentina | 2013 | 13.07 |
| 163 | Argentina | 2014 | 13.30 |
| 208 | Argentina | 2015 | 14.61 |
| ... | ... | ... | ... |
| 233 | Vietnam | 2016 | 5.98 |
| 278 | Vietnam | 2017 | 5.78 |
| 323 | Vietnam | 2018 | 6.54 |
| 368 | Vietnam | 2019 | 7.59 |
| 413 | Vietnam | 2020 | 11.53 |
450 rows × 3 columns
Посмотрим на boxplot по столбцу "Average length of stay (days)":
fig = px.box(stay_len, y="Average length of stay (days)", color_discrete_sequence=['purple'])
fig.update_layout(title="Среднее время пребывания туриста (в днях)", title_x=0.5)
fig.show()
df = pd.read_excel(io=r"country_visit_freq.xlsx", engine='openpyxl')
df.Country = df.Country.apply(lambda x: x.replace("\xa0", ' '))
df.sample(10)
| Continent | Country | Frequency of Visit | 2011 | 2012 | 2013 | 2014 | 2015 | 2016 | 2017 | 2018 | 2019 | 2020 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 135 | Oceania | Others | Total | 6633 | 6599 | 6634 | 5919 | 5562 | 6119 | 2739 | 7917 | 8616 | 1458 |
| 27 | East Asia | China | Total | 1704800 | 2761213 | 4609717 | 4631981 | 7981407 | 8821148 | 9846818 | 10625167 | 11138658 | 1302137 |
| 70 | Europe | Netherlands | First visit | 59464 | 65514 | 76947 | 72267 | 78559 | 78653 | 70718 | 70995 | 75762 | 13876 |
| 102 | The Americas | Canada | Total | 170981 | 192602 | 209059 | 195429 | 210097 | 222358 | 74778 | 252763 | 252574 | 55984 |
| 97 | The Americas | Argentina | First visit | 7252 | 10800 | 13208 | 13199 | 16443 | 21812 | 33755 | 17678 | 10871 | 4604 |
| 98 | The Americas | Argentina | Re-visit | 4753 | 6095 | 6874 | 7209 | 11607 | 24578 | 62738 | 25807 | 17312 | 7857 |
| 99 | The Americas | Brazil | Total | 21231 | 26997 | 34176 | 43694 | 44414 | 63139 | 31995 | 57105 | 58970 | 18200 |
| 82 | Europe | Sweden | First visit | 80375 | 71109 | 74056 | 69681 | 79238 | 92917 | 67760 | 41593 | 50134 | 19717 |
| 156 | Africa | Republic of South Africa | Total | 66705 | 73530 | 73657 | 70574 | 74397 | 76541 | 41329 | 97071 | 89750 | 9692 |
| 53 | Europe | Denmark | Re-visit | 116417 | 118205 | 113319 | 112656 | 108672 | 112452 | 116655 | 136231 | 120314 | 48173 |
Пропусков в данных нет
df.isna().sum()
Continent 0 Country 0 Frequency of Visit 0 2011 0 2012 0 2013 0 2014 0 2015 0 2016 0 2017 0 2018 0 2019 0 2020 0 dtype: int64
В таблице содержится информация по следующим странам
df.Country.sort_values().unique()
array(['Argentina', 'Australia', 'Austria', 'Bangladesh', 'Belgium',
'Brazil', 'Brunai', 'Cambodia', 'Canada', 'China', 'Denmark',
'East Europe', 'Egypt', 'Finland', 'France', 'Germany',
'Hong Kong', 'India', 'Indonesia', 'Ireland', 'Israel', 'Italy',
'Japan', 'Kuwait', 'Laos', 'Malaysia', 'Myanmar', 'Nepal',
'Netherlands', 'New Zealand', 'Norway', 'Others', 'Pakistan',
'Philippines', 'Republic of South Africa', 'Russia',
'Saudi Arabia', 'Singapore', 'South Korea', 'Spain', 'Sri Lanka',
'Sweden', 'Switzerland', 'Taiwan', 'U.S.A.',
'United Arab Emirates', 'United Kingdom', 'Vietnam'], dtype=object)
Нужно исключить Ирландию из столбца "Country", так как этой страны нет в других таблицах.
df = df.query("Country not in ('Ireland','East Europe','Others')")
visit_freq = pd.melt(df, id_vars=['Country', 'Frequency of Visit'], value_vars=range(2011, 2021), var_name='Year', value_name='Arrivals').sort_values(['Country', 'Year'])
visit_freq
| Country | Frequency of Visit | Year | Arrivals | |
|---|---|---|---|---|
| 84 | Argentina | Total | 2011 | 12005 |
| 85 | Argentina | First visit | 2011 | 7252 |
| 86 | Argentina | Re-visit | 2011 | 4753 |
| 219 | Argentina | Total | 2012 | 16895 |
| 220 | Argentina | First visit | 2012 | 10800 |
| ... | ... | ... | ... | ... |
| 1105 | Vietnam | First visit | 2019 | 361973 |
| 1106 | Vietnam | Re-visit | 2019 | 718636 |
| 1239 | Vietnam | Total | 2020 | 131308 |
| 1240 | Vietnam | First visit | 2020 | 35654 |
| 1241 | Vietnam | Re-visit | 2020 | 95654 |
1350 rows × 4 columns
Посмотрим на boxplot по столбцу "Arrivals", сгрупированному по столбцу "Frequency of Visit":
fig = px.box(visit_freq[visit_freq["Frequency of Visit"] != 'Total'], x="Frequency of Visit", y="Arrivals", color="Frequency of Visit", color_discrete_sequence=['purple', 'green', 'yellow'])
fig.update_layout(title="Количество прибытий туристов в год: первичные/повторные", title_x=0.5, showlegend=False)
fig.show()
В столбце "Frequency of Visit" значение "Total" (общее кол-во) является избыточной информацией, его необходимо удалить.
visit_freq = visit_freq.pivot(columns="Frequency of Visit", index=['Country', 'Year'], values='Arrivals').reset_index()
visit_freq.drop("Total", axis=1, inplace=True)
visit_freq
| Frequency of Visit | Country | Year | First visit | Re-visit |
|---|---|---|---|---|
| 0 | Argentina | 2011 | 7252 | 4753 |
| 1 | Argentina | 2012 | 10800 | 6095 |
| 2 | Argentina | 2013 | 13208 | 6874 |
| 3 | Argentina | 2014 | 13199 | 7209 |
| 4 | Argentina | 2015 | 16443 | 11607 |
| ... | ... | ... | ... | ... |
| 445 | Vietnam | 2016 | 341267 | 522540 |
| 446 | Vietnam | 2017 | 369763 | 615932 |
| 447 | Vietnam | 2018 | 395049 | 658674 |
| 448 | Vietnam | 2019 | 361973 | 718636 |
| 449 | Vietnam | 2020 | 35654 | 95654 |
450 rows × 4 columns
"""
countries = []
for country in visit_freq.Country.unique():
countries.extend([country]*10)
years = []
for _ in range(len(visit_freq.Country.unique())):
years.extend(visit_freq.Year.unique())
total_visit = visit_freq[visit_freq['Frequency of Visit'] == 'Total'].Arrivals.to_list()
first_visit = visit_freq[visit_freq['Frequency of Visit'] == 'First visit'].Arrivals.to_list()
re_visit = visit_freq[visit_freq['Frequency of Visit'] == 'Re-visit'].Arrivals.to_list()
calc_share = lambda x: first_visit[x] / total_visit[x] if total_visit[x] > re_visit[x] else first_visit[x] / re_visit[x]
first_share = [calc_share(i) if total_visit[i] != 0 and re_visit[i] != 0 else 0 for i in range(len(total_visit))]
first_time_visit = pd.DataFrame({'Country': countries, 'Year': years, 'Share of first time visits': first_visit})
first_time_visit.Country.unique()
"""
"\ncountries = []\nfor country in visit_freq.Country.unique():\n countries.extend([country]*10)\n \nyears = []\nfor _ in range(len(visit_freq.Country.unique())):\n years.extend(visit_freq.Year.unique())\n\ntotal_visit = visit_freq[visit_freq['Frequency of Visit'] == 'Total'].Arrivals.to_list()\nfirst_visit = visit_freq[visit_freq['Frequency of Visit'] == 'First visit'].Arrivals.to_list()\nre_visit = visit_freq[visit_freq['Frequency of Visit'] == 'Re-visit'].Arrivals.to_list()\n\ncalc_share = lambda x: first_visit[x] / total_visit[x] if total_visit[x] > re_visit[x] else first_visit[x] / re_visit[x]\nfirst_share = [calc_share(i) if total_visit[i] != 0 and re_visit[i] != 0 else 0 for i in range(len(total_visit))]\n\nfirst_time_visit = pd.DataFrame({'Country': countries, 'Year': years, 'Share of first time visits': first_visit})\nfirst_time_visit.Country.unique()\n"
df = pd.read_excel(io=r"arrival_transport.xlsx", engine='openpyxl')
df.Country = df.Country.apply(lambda x: x.replace("\xa0", ' '))
df.sample(10)
| Continent | Country | Mode of Transport | 2011 | 2012 | 2013 | 2014 | 2015 | 2016 | 2017 | 2018 | 2019 | 2020 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 29 | East Asia | Singapore | Air | 594644 | 753034 | 856167 | 794482 | 1029014 | 928077 | 998565 | 1037945 | 1025492 | 122772 |
| 198 | Middle East | Saudi Arabia | Land | 74 | 114 | 195 | 155 | 217 | 256 | 202 | 141 | 254 | 6 |
| 129 | The Americas | Argentina | Air | 10829 | 15096 | 18358 | 19340 | 25952 | 42861 | 60399 | 42196 | 27930 | 11658 |
| 145 | The Americas | Others | Air | 33520 | 43074 | 49079 | 53460 | 55421 | 70172 | 82472 | 87856 | 89939 | 17036 |
| 68 | Europe | Denmark | Total | 164096 | 167499 | 163186 | 160977 | 159420 | 165581 | 161920 | 169365 | 162448 | 66848 |
| 177 | Oceania | New Zealand | Air | 93517 | 105657 | 109814 | 102076 | 97208 | 106150 | 113649 | 112911 | 109397 | 14985 |
| 147 | The Americas | Others | Waterway | 2093 | 1283 | 2401 | 646 | 680 | 677 | 754 | 667 | 656 | 362 |
| 174 | Oceania | Australia | Land | 28524 | 30625 | 29648 | 24555 | 21351 | 20025 | 15027 | 13541 | 12756 | 1578 |
| 186 | Middle East | Egypt | Land | 86 | 70 | 164 | 66 | 107 | 97 | 120 | 112 | 106 | 12 |
| 131 | The Americas | Argentina | Waterway | 765 | 865 | 665 | 400 | 460 | 339 | 383 | 389 | 452 | 270 |
Пропусков в данных нет
df.isna().sum()
Continent 0 Country 0 Mode of Transport 0 2011 0 2012 0 2013 0 2014 0 2015 0 2016 0 2017 0 2018 0 2019 0 2020 0 dtype: int64
Посмотрим на boxplot по столбцу "Arrivals", сгрупированному по столбцу "Frequency of Visit":
df = df.query("Country not in ('Ireland','East Europe','Others')")
transport_type = pd.melt(df, id_vars=['Country', 'Mode of Transport'], value_vars=range(2011, 2021), var_name='Year', value_name='Arrivals').sort_values(['Country', 'Year'])
fig = px.box(transport_type[transport_type["Mode of Transport"] != 'Total'], x="Mode of Transport", y="Arrivals", color="Mode of Transport", color_discrete_sequence=['purple', 'green', 'yellow'])
fig.update_layout(title="Количество прибытий туристов в год: по воздуху/по суше/по воде", title_x=0.5, showlegend=False)
fig.show()
Из столбца "Mode of Transport" следует исключить избыточную категорию "Total".
transport_type = transport_type.pivot(columns='Mode of Transport', index=['Country', 'Year'], values='Arrivals').reset_index()
transport_type.drop('Total', axis=1, inplace=True)
transport_type
| Mode of Transport | Country | Year | Air | Land | Waterway |
|---|---|---|---|---|---|
| 0 | Argentina | 2011 | 10829 | 1376 | 765 |
| 1 | Argentina | 2012 | 15096 | 1892 | 865 |
| 2 | Argentina | 2013 | 18358 | 2012 | 665 |
| 3 | Argentina | 2014 | 19340 | 1698 | 400 |
| 4 | Argentina | 2015 | 25952 | 1638 | 460 |
| ... | ... | ... | ... | ... | ... |
| 445 | Vietnam | 2016 | 562358 | 219252 | 48610 |
| 446 | Vietnam | 2017 | 687299 | 201228 | 46652 |
| 447 | Vietnam | 2018 | 785034 | 197572 | 44654 |
| 448 | Vietnam | 2019 | 821096 | 186800 | 39767 |
| 449 | Vietnam | 2020 | 103654 | 6638 | 21835 |
450 rows × 5 columns
df = pd.read_excel(io=r"country_expenditure_type.xlsx", engine='openpyxl')
df.Country = df.Country.apply(lambda x: x.replace("\xa0", ' '))
df.sample(10)
| Continent | Country | Expenditure Item | 2011 | 2012 | 2013 | 2014 | 2015 | 2016 | 2017 | 2018 | 2019 | 2020 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 518 | Africa | Republic of South Africa | Miscellanous | 51.01 | 56.75 | 56.25 | 61.76 | 46.12 | 65.88 | 67.75 | 66.46 | 74.00 | 48.28 |
| 17 | East Asia | Cambodia | Entertainment | 568.26 | 573.24 | 543.35 | 543.58 | 599.22 | 625.88 | 882.23 | 200.44 | 217.44 | 201.68 |
| 498 | Middle East | United Arab Emirates | Miscellanous | 64.77 | 71.05 | 65.85 | 79.55 | 73.14 | 75.26 | 88.37 | 98.24 | 109.40 | 109.17 |
| 293 | Europe | East Europe | Food and beverage | 788.02 | 823.83 | 869.72 | 929.65 | 956.07 | 998.67 | 999.95 | 1011.25 | 1020.52 | 731.19 |
| 84 | East Asia | Vietnam | Sight seeing | 145.72 | 160.88 | 156.39 | 139.84 | 144.76 | 146.58 | 149.81 | 137.98 | 133.49 | 115.48 |
| 347 | The Americas | U.S.A. | Entertainment | 595.92 | 604.11 | 600.73 | 615.63 | 602.02 | 650.02 | 623.70 | 530.17 | 465.21 | 201.46 |
| 244 | Europe | Russia | Sight seeing | 196.34 | 213.69 | 217.21 | 202.28 | 173.22 | 173.32 | 153.54 | 154.41 | 155.27 | 116.52 |
| 179 | Europe | Denmark | Medical Care | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 0.00 | 67.53 | 41.46 | 18.48 |
| 76 | East Asia | Singapore | Shopping | 1472.14 | 1560.87 | 1657.86 | 1841.34 | 1944.41 | 1935.59 | 1860.68 | 1862.44 | 1713.63 | 1083.06 |
| 522 | Africa | Others | Accommodation | 1173.94 | 1243.83 | 1331.45 | 1405.76 | 1484.79 | 1529.86 | 1570.27 | 1481.48 | 1444.60 | 946.13 |
Пропусков в данных нет
df.isna().sum()
Continent 0 Country 0 Expenditure Item 0 2011 0 2012 0 2013 0 2014 0 2015 0 2016 0 2017 0 2018 0 2019 0 2020 0 dtype: int64
В таблице содержится информация по следующим категориям расходов
df["Expenditure Item"].sort_values().unique()
array(['Accommodation', 'Entertainment', 'Food and beverage',
'Local transport', 'Medical Care', 'Miscellanous', 'Shopping',
'Sight seeing', 'Total ($US)', 'Total (Baht)'], dtype=object)
Посмотрим на boxplot по столбцу "Expenditure (THB)", сгрупированному по столбцу "Expenditure Item":
df = df.query("Country not in ('Ireland','East Europe','Others')")
expenditure = pd.melt(df, id_vars=['Country', 'Expenditure Item'], value_vars=range(2011, 2021), var_name='Year', value_name='Expenditure (THB)').sort_values(['Country', 'Year'])
fig = px.box(expenditure[~expenditure["Expenditure Item"].isin(['Total ($US)', 'Total (Baht)'])],
x="Expenditure Item", y="Expenditure (THB)", color="Expenditure Item",
color_discrete_sequence=['purple', 'green', 'yellow', 'plum', 'yellowgreen', 'lemonchiffon', 'orchid', 'olive'])
fig.update_layout(title="Средняя сумма ежедневных расходов по категориям ", title_x=0.5, showlegend=False)
fig.show()
Из столбца "Expenditure Item" следует исключить избыточные категории "Total ($US)" и "Total (Baht)".
expenditure = expenditure.pivot(columns='Expenditure Item', index=['Country', 'Year'], values='Expenditure (THB)').reset_index()
expenditure.drop(['Total ($US)', 'Total (Baht)'], axis=1, inplace=True)
expenditure
| Expenditure Item | Country | Year | Accommodation | Entertainment | Food and beverage | Local transport | Medical Care | Miscellanous | Shopping | Sight seeing |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | Argentina | 2011 | 1360.47 | 551.13 | 820.26 | 540.01 | 0.00 | 55.88 | 838.16 | 124.28 |
| 1 | Argentina | 2012 | 1395.18 | 557.54 | 824.53 | 563.98 | 0.00 | 59.38 | 854.34 | 134.70 |
| 2 | Argentina | 2013 | 1440.00 | 550.91 | 868.63 | 589.59 | 0.00 | 51.89 | 856.13 | 137.31 |
| 3 | Argentina | 2014 | 1503.65 | 545.38 | 919.44 | 620.20 | 0.00 | 49.59 | 916.60 | 158.42 |
| 4 | Argentina | 2015 | 1512.55 | 508.94 | 981.65 | 651.20 | 0.00 | 43.09 | 890.92 | 154.51 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 445 | Vietnam | 2016 | 1315.21 | 434.01 | 967.18 | 447.71 | 0.00 | 71.76 | 1741.16 | 146.58 |
| 446 | Vietnam | 2017 | 1248.62 | 384.14 | 1042.06 | 444.97 | 0.00 | 77.56 | 1857.89 | 149.81 |
| 447 | Vietnam | 2018 | 1205.15 | 343.73 | 1104.68 | 435.45 | 53.46 | 78.27 | 1729.02 | 137.98 |
| 448 | Vietnam | 2019 | 1191.74 | 346.79 | 1016.32 | 415.64 | 77.35 | 69.32 | 1591.58 | 133.49 |
| 449 | Vietnam | 2020 | 1118.62 | 130.64 | 904.05 | 335.26 | 77.78 | 59.01 | 991.38 | 115.48 |
450 rows × 10 columns
Загрузим данные и отберем те, что соответствуют нужным годам.
df = pd.read_excel(io=r"world_gdp.xls")
df.drop(labels=['Country Code', 2021] + list(range(1960, 2011)), axis=1, inplace=True)
df.sample(10)
| Country Name | 2011 | 2012 | 2013 | 2014 | 2015 | 2016 | 2017 | 2018 | 2019 | 2020 | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 67 | Egypt | 2.359897e+11 | 2.791167e+11 | 2.884341e+11 | 3.055954e+11 | 3.293666e+11 | 3.324417e+11 | 2.357337e+11 | 2.497130e+11 | 3.030809e+11 | 3.652527e+11 |
| 205 | Sao Tome and Principe | 2.314893e+08 | 2.506808e+08 | 3.005545e+08 | 3.465283e+08 | 3.160661e+08 | 3.454956e+08 | 3.756141e+08 | 4.122538e+08 | 4.274250e+08 | 4.725510e+08 |
| 57 | Czechia | 2.295627e+11 | 2.088577e+11 | 2.116856e+11 | 2.093588e+11 | 1.880331e+11 | 1.962721e+11 | 2.186289e+11 | 2.490005e+11 | 2.525482e+11 | 2.459746e+11 |
| 189 | Panama | 3.468622e+10 | 4.042970e+10 | 4.559990e+10 | 4.992140e+10 | 5.409180e+10 | 5.790770e+10 | 6.220270e+10 | 6.492940e+10 | 6.698440e+10 | 5.397700e+10 |
| 183 | OECD members | 4.867149e+13 | 4.861931e+13 | 4.926724e+13 | 5.027546e+13 | 4.740883e+13 | 4.832536e+13 | 5.044393e+13 | 5.336583e+13 | 5.387859e+13 | 5.251705e+13 |
| 90 | Gibraltar | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 75 | Europe & Central Asia | 2.330552e+13 | 2.244977e+13 | 2.345250e+13 | 2.377971e+13 | 2.048415e+13 | 2.041863e+13 | 2.165394e+13 | 2.319356e+13 | 2.290998e+13 | 2.213998e+13 |
| 253 | United Kingdom | 2.666403e+12 | 2.706341e+12 | 2.786315e+12 | 3.065223e+12 | 2.934858e+12 | 2.699660e+12 | 2.683399e+12 | 2.878152e+12 | 2.857058e+12 | 2.704609e+12 |
| 109 | IDA only | 8.856042e+11 | 8.912987e+11 | 9.673684e+11 | 1.033527e+12 | 1.039334e+12 | 1.120362e+12 | 1.233428e+12 | 1.229623e+12 | 1.297064e+12 | 1.321285e+12 |
| 158 | Middle East & North Africa | 3.354142e+12 | 3.627526e+12 | 3.565646e+12 | 3.592212e+12 | 3.164191e+12 | 3.192322e+12 | 3.313255e+12 | 3.477249e+12 | 3.472361e+12 | 3.115193e+12 |
Пропусков в данных нет
df.isna().sum()
Country Name 0 2011 6 2012 8 2013 7 2014 6 2015 8 2016 9 2017 9 2018 9 2019 11 2020 14 dtype: int64
gdp = pd.melt(df, id_vars=['Country Name'], value_vars=range(2011, 2021), var_name='Year', value_name='GDP').rename(columns={'Country Name': 'Country'}).sort_values(['Country', 'Year'])
gdp.Country = gdp.Country.apply(lambda x: x.replace("\xa0", ' '))
gdp.sample(10)
| Country | Year | GDP | |
|---|---|---|---|
| 1772 | Niger | 2017 | 1.118510e+10 |
| 2231 | Hong Kong | 2019 | 3.630525e+11 |
| 1053 | Uruguay | 2014 | 5.723601e+10 |
| 1756 | Middle East & North Africa (IDA & IBRD countries) | 2017 | 1.448152e+12 |
| 2646 | United Arab Emirates | 2020 | 3.494730e+11 |
| 512 | Turkmenistan | 2012 | 3.516421e+10 |
| 1446 | Isle of Man | 2016 | 6.846692e+09 |
| 267 | Africa Eastern and Southern | 2012 | 9.725734e+11 |
| 279 | Australia | 2012 | 1.546892e+12 |
| 1532 | Rwanda | 2016 | 8.690878e+09 |
Посмотрим на boxplot по столбцу "GDP":
fig = px.box(gdp, y="GDP", color_discrete_sequence=['purple'])
fig.update_layout(title="Годовой ВВП", title_x=0.5)
fig.show()
final_data = pd.merge(stay_len, visit_freq, on=['Country','Year'])
final_data = pd.merge(final_data, transport_type, on=['Country','Year'])
final_data = pd.merge(final_data, expenditure, on=['Country','Year'])
final_data = pd.merge(final_data, gdp, on=['Country','Year'])
final_data
| Country | Year | Average length of stay (days) | First visit | Re-visit | Air | Land | Waterway | Accommodation | Entertainment | Food and beverage | Local transport | Medical Care | Miscellanous | Shopping | Sight seeing | GDP | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | Argentina | 2011 | 11.91 | 7252 | 4753 | 10829 | 1376 | 765 | 1360.47 | 551.13 | 820.26 | 540.01 | 0.00 | 55.88 | 838.16 | 124.28 | 5.301633e+11 |
| 1 | Argentina | 2012 | 12.51 | 10800 | 6095 | 15096 | 1892 | 865 | 1395.18 | 557.54 | 824.53 | 563.98 | 0.00 | 59.38 | 854.34 | 134.70 | 5.459824e+11 |
| 2 | Argentina | 2013 | 13.07 | 13208 | 6874 | 18358 | 2012 | 665 | 1440.00 | 550.91 | 868.63 | 589.59 | 0.00 | 51.89 | 856.13 | 137.31 | 5.520251e+11 |
| 3 | Argentina | 2014 | 13.30 | 13199 | 7209 | 19340 | 1698 | 400 | 1503.65 | 545.38 | 919.44 | 620.20 | 0.00 | 49.59 | 916.60 | 158.42 | 5.263197e+11 |
| 4 | Argentina | 2015 | 14.61 | 16443 | 11607 | 25952 | 1638 | 460 | 1512.55 | 508.94 | 981.65 | 651.20 | 0.00 | 43.09 | 890.92 | 154.51 | 5.947493e+11 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 435 | Vietnam | 2016 | 5.98 | 341267 | 522540 | 562358 | 219252 | 48610 | 1315.21 | 434.01 | 967.18 | 447.71 | 0.00 | 71.76 | 1741.16 | 146.58 | 2.570960e+11 |
| 436 | Vietnam | 2017 | 5.78 | 369763 | 615932 | 687299 | 201228 | 46652 | 1248.62 | 384.14 | 1042.06 | 444.97 | 0.00 | 77.56 | 1857.89 | 149.81 | 2.813536e+11 |
| 437 | Vietnam | 2018 | 6.54 | 395049 | 658674 | 785034 | 197572 | 44654 | 1205.15 | 343.73 | 1104.68 | 435.45 | 53.46 | 78.27 | 1729.02 | 137.98 | 3.101065e+11 |
| 438 | Vietnam | 2019 | 7.59 | 361973 | 718636 | 821096 | 186800 | 39767 | 1191.74 | 346.79 | 1016.32 | 415.64 | 77.35 | 69.32 | 1591.58 | 133.49 | 3.343653e+11 |
| 439 | Vietnam | 2020 | 11.53 | 35654 | 95654 | 103654 | 6638 | 21835 | 1118.62 | 130.64 | 904.05 | 335.26 | 77.78 | 59.01 | 991.38 | 115.48 | 3.466158e+11 |
440 rows × 17 columns
Получаем полный датасет. Теперь будет удобнее сравнивать различные показатели.
final_data.describe()
| Average length of stay (days) | First visit | Re-visit | Air | Land | Waterway | Accommodation | Entertainment | Food and beverage | Local transport | Medical Care | Miscellanous | Shopping | Sight seeing | GDP | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| count | 440.000000 | 4.400000e+02 | 4.400000e+02 | 4.400000e+02 | 4.400000e+02 | 440.000000 | 440.000000 | 440.000000 | 440.000000 | 440.000000 | 440.000000 | 440.000000 | 440.000000 | 440.000000 | 4.400000e+02 |
| mean | 12.107386 | 2.234573e+05 | 3.747420e+05 | 4.831458e+05 | 9.486607e+04 | 12827.597727 | 1439.339068 | 495.309705 | 955.103591 | 492.780250 | 38.264614 | 64.414614 | 1180.075705 | 152.373795 | 1.563401e+12 |
| std | 4.494754 | 6.174363e+05 | 6.176659e+05 | 1.095184e+06 | 3.479132e+05 | 35206.136234 | 245.995951 | 167.006642 | 175.490379 | 75.711605 | 136.741247 | 21.013600 | 495.795351 | 45.530000 | 3.247911e+12 |
| min | 4.640000 | 4.160000e+02 | 1.862000e+03 | 1.612000e+03 | 0.000000e+00 | 1.000000 | 638.910000 | 40.030000 | 400.330000 | 178.950000 | 0.000000 | 15.020000 | 250.830000 | 24.810000 | 8.750107e+09 |
| 25% | 7.680000 | 2.680075e+04 | 5.318325e+04 | 7.156525e+04 | 1.015500e+03 | 458.000000 | 1264.762500 | 394.737500 | 832.032500 | 446.972500 | 0.000000 | 48.987500 | 762.825000 | 121.300000 | 2.700458e+11 |
| 50% | 12.805000 | 6.034150e+04 | 1.311165e+05 | 1.595665e+05 | 5.311000e+03 | 1824.500000 | 1416.735000 | 477.915000 | 935.545000 | 488.875000 | 0.000000 | 60.630000 | 1060.370000 | 152.010000 | 4.330429e+11 |
| 75% | 16.405000 | 2.146445e+05 | 4.905412e+05 | 6.108315e+05 | 3.433400e+04 | 7956.500000 | 1592.697500 | 579.862500 | 1059.787500 | 538.540000 | 24.635000 | 77.617500 | 1590.792500 | 178.175000 | 1.549296e+12 |
| max | 20.280000 | 5.762730e+06 | 5.469469e+06 | 1.065960e+07 | 2.825754e+06 | 420769.000000 | 2173.990000 | 1222.800000 | 1477.980000 | 745.960000 | 1234.870000 | 156.050000 | 2388.260000 | 347.340000 | 2.138098e+13 |
final_data.to_csv("Thailand_Tourism_2011_2020.csv")
С увеличением среднего времени пербывания в странe изменяется структура ежедневных трат по категориям.
Иными словами, существует корелляция между средним количеством дней пребывания и долями категорий от средней суммы ежедневных расходов.
Предварительно нужно перевести абсолютное значение расходов по категориям трат в относительное.
total_daily_expenditure = (final_data.Accommodation + final_data.Entertainment + final_data["Food and beverage"] +
final_data["Local transport"] + final_data["Medical Care"] + final_data.Miscellanous +
final_data.Shopping + final_data["Sight seeing"])
relative_daily_expenditure = final_data.iloc[:, 8:16].apply(lambda x: x / total_daily_expenditure)
h1_data = pd.merge(final_data["Average length of stay (days)"], relative_daily_expenditure, left_index=True, right_index=True)
h1_data.corr().iloc[:,0]
Average length of stay (days) 1.000000 Accommodation 0.611256 Entertainment 0.130424 Food and beverage 0.650510 Local transport 0.688258 Medical Care -0.019858 Miscellanous -0.347299 Shopping -0.789257 Sight seeing 0.264714 Name: Average length of stay (days), dtype: float64
Получили коэффициенты корелляции между средним количеством дней пребывания и долями категорий от средней суммы ежедневных расходов. Видим, что существует положительная линейная зависимость между кол-вом дней пребывания и долей ежедневных трат на категории "Жилье", "Еда и напитки", "Внутренный транспорт". С другой стороны, есть сильная отрицательная линейная зависимость между кол-вом дней пребывания и долей ежедневных трат на категорию "Шоппинг".
Таким образом, можно предположить, что в Таиланде туристы с коротким сроком пребывания больше тратят на покупки, а с увеличением кол-ва дней отпуска увеличивается доля трат на бытовые нужды: жилье, еду, транспорт (при долгом нахождении в стране образ жизни становится более рутинным и схожим с местными жителями).
Средняя сумма ежедневных трат на жилье подчиняется нормальному распределению.
Взглянем на гистограмму.
fig = px.histogram(final_data["Accommodation"],
histnorm='probability',
color_discrete_sequence=['purple'])
fig.update_layout(title="Плотность распределения ежедневных трат на жилье",
title_x=0.5, showlegend=False, xaxis=None, yaxis=None)
fig.show()
Визуально по форме графика можно предположить, что средняя ежедневная сумма расходов на жилье имеет нормальное распределение. Но еще стоит проверить гипотезу о нормальности распределения, используя критерий $\chi^2$ Пирсона с уровнем значимости $\alpha = 0.05$.
_, p_value = scipy.stats.normaltest(final_data.Accommodation)
p_value
0.01982729899262304
$p$-значение меньше уровня значимости $\alpha = 0.05$, значит гипотеза о нормальности распределения отвергается. То есть средняя сумма ежедневных трат не подчиняется нормальному распределению.
Значения годового ВВП стран, откуда прибывают туристы, и количество прибытий из этих стран в год распределены одинаково.
Мировой ВВП среди стран распространен неравномерно. Есть несколько стран с высоким ВВП, составляющим большую часть мирвого. Остальной мировой ВВП распределен между сотнями других стран с низким ВВП.
Есть предположение, что кол-во прибытий туристов подчиняется схожему закону: есть несколько стран, отвечающих за большую часть турпотока, и множество стран, откуда прибывает относительно немного туристов.
Проверим это, сравнив распределения выборок данных величин.
Предварительно лучше нормализовать значения выборок, чтобы они лежали в одинаковых границах и были сопоставимы.
gdp_norm = (final_data["GDP"] - min(final_data["GDP"])) / (max(final_data["GDP"]) - min(final_data["GDP"]))
visits_norm = final_data["First visit"] + final_data["Re-visit"]
visits_norm = (visits_norm - min(visits_norm)) / (max(visits_norm) - min(visits_norm))
fig1 = px.histogram(gdp_norm,
histnorm='probability', nbins=21,
color_discrete_sequence=['purple'])
fig1.update_layout(title="Плотность распределения ежегодного ВВП стран-источников турпотока",
title_x=0.5, showlegend=False, xaxis=None, yaxis=None)
fig1.show()
fig2 = px.histogram(visits_norm,
histnorm='probability', nbins=21,
color_discrete_sequence=['purple'])
fig2.update_layout(title="Плотность распределения размера ежегодного турпотока из каждой страны",
title_x=0.5, showlegend=False, xaxis=None, yaxis=None)
fig2.show()
Исходя из визуального анализа гистограмм можно увидеть, что распределения двух выборок очень похожи. Используем критерий Колмогорова-Смирнова для статистической проверки нашей гипотезы. Уровень значимости $\alpha = 0.05$.
_, p_value = scipy.stats.kstest(gdp_norm, visits_norm)
p_value
9.776477159692956e-05
$p$-значение меньше уровня значимости $\alpha = 0.05$, значит гипотеза об однородности распределений двух выборок отвергается. То есть значения годового ВВП стран-источников турпотока и количество прибытий из этих стран в год имеют различные распределения.
В качестве продукта этого разведочного анализа было решено создать интерактивный user-friendly дашборд
из нескольких графиков и карт, визуализирующих исследуемые данные. Основной инструмент - Tableau.
Дашборд доступен по ссылке.